package yartiss.gui.main;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingQueue;

import javax.swing.BoundedRangeModel;
import javax.swing.DefaultBoundedRangeModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTabbedPane;
import javax.swing.SwingWorker;

import yartiss.engine.simulation.IEnergyProfile;
import yartiss.engine.simulation.ISchedulingPolicy;
import yartiss.engine.simulation.Simulation;
import yartiss.engine.simulation.SimulationParameters;
import yartiss.engine.simulation.energy.profiles.EnergyProfileFactory;
import yartiss.engine.simulation.events.DeadlineMissedEvent;
import yartiss.engine.simulation.events.EnergyFailureEvent;
import yartiss.engine.simulation.events.EventVisitorAdapter;
import yartiss.engine.simulation.io.TasksetReader;
import yartiss.engine.simulation.scheduling.policies.SchedulingPolicyFactory;
import yartiss.engine.simulation.system.ITask;
import yartiss.engine.simulation.system.ITaskSet;
import yartiss.framework.concurrency.Consumer;
import yartiss.framework.concurrency.IElementConsumedListener;
import yartiss.framework.concurrency.Producer;
import yartiss.framework.concurrency.QueueEntry;
import yartiss.gui.graphics.JChartPane;
import yartiss.gui.graphics.rendrerers.BarChartRenderer;
import yartiss.gui.graphics.rendrerers.LineChartRenderer;
import yartiss.service.model.Metric;
import yartiss.service.model.MetricsChartModel;
import yartiss.service.model.Scenario;
import yartiss.service.model.ScenariosChartModel;
import yartiss.service.model.TasksSetsTableModel;

public class Huge {

	private static final int simulationDuration = 100000;
	private final BlockingQueue<QueueEntry<Simulation>> queue = new LinkedBlockingQueue<QueueEntry<Simulation>>(10000);
	private Consumer<Simulation> consumer;
	private Producer<Simulation> producer;
	private String systemsFile;
	private IEnergyProfile[] energyProfiles;
	private ScenariosChartModel scenariosModel;
	private MetricsChartModel metricsModel;
	private final BoundedRangeModel progressModel = new DefaultBoundedRangeModel(0, 1, 0, 100000);
	private final static String [] metrcisNames = new String[]{"Failure","sim duration","Tmax","Tmin","BP","IP"};
	private boolean isDebug = true; 

	public Huge(String systemsFile){
		this.systemsFile = systemsFile; 

		energyProfiles = new  IEnergyProfile[] {	
				EnergyProfileFactory.getInstance("defaultEnergyProfile", 10, 200, 0, 0),
				EnergyProfileFactory.getInstance("defaultEnergyProfile", 15, 200, 0, 0),
				EnergyProfileFactory.getInstance("defaultEnergyProfile", 20, 200, 0, 0),
				EnergyProfileFactory.getInstance("defaultEnergyProfile", 25, 200, 0, 0),
				EnergyProfileFactory.getInstance("defaultEnergyProfile", 30, 200, 0, 0),

//				EnergyProfileFactory.getInstance("defaultEnergyProfile", 35, 200, 0, 0),

				EnergyProfileFactory.getInstance("defaultEnergyProfile", 10, 400, 0, 0),
				EnergyProfileFactory.getInstance("defaultEnergyProfile", 15, 400, 0, 0),
				EnergyProfileFactory.getInstance("defaultEnergyProfile", 20, 400, 0, 0),
				EnergyProfileFactory.getInstance("defaultEnergyProfile", 25, 400, 0, 0),
				EnergyProfileFactory.getInstance("defaultEnergyProfile", 30, 400, 0, 0),
//				EnergyProfileFactory.getInstance("defaultEnergyProfile", 35, 400, 0, 0),

		};

		setModel(new ScenariosChartModel());

		for (IEnergyProfile profile : energyProfiles) {
			Scenario scenario = new Scenario(profile, simulationDuration);
			getScenarioModel().add(scenario,scenario.getScenarioName());
		}
		metricsModel = new  MetricsChartModel();
		for (String metric : metrcisNames) {
			metricsModel.add(new Metric(metric), metric);
		}
	}

	class EventsVisitor extends EventVisitorAdapter{
		public yartiss.service.model.TasksSetsTableModel.Result result = yartiss.service.model.TasksSetsTableModel.Result.SUCCESS;
		@Override
		public void visit(EnergyFailureEvent e) {
			result = yartiss.service.model.TasksSetsTableModel.Result.ENERGY_FAILURE;
		}

		@Override
		public void visit(DeadlineMissedEvent e) {
			result = yartiss.service.model.TasksSetsTableModel.Result.DEADLINE_MISSED;
		}

	}

	private void updateDebugResults(Simulation sim, Scenario senario){
		TasksSetsTableModel model = senario.getSetsModel();
		EventsVisitor visitor = new EventsVisitor();
		sim.getStatistics().getLastEvent().accept(visitor);
		model.updateEntry(sim.getSimulationParameters().getTaskSet().getId(), sim.getSimulationParameters().getSchedulingPolicy().getPolicyName(),sim.getSimulationParameters().getTaskSet(),senario.getProfile(),simulationDuration,  visitor.result);
	}

	public void run() throws Exception{
		SwingWorker<Void, String> task = new SwingWorker<Void, String>() {
			@Override	protected Void doInBackground() throws Exception {
				producer = new Producer<Simulation>(queue);
				consumer = new Consumer<Simulation>(queue, new IElementConsumedListener<Simulation>() {
					@Override
					public void consume(Simulation sim) {
						String scenarioName = sim.getSimulationParameters().getEnergyProfile().getMaxEnergyLevel()+"_"+sim.getSimulationParameters().getEnergyProfile().getPowerRate();
						Scenario scenario = getScenarioModel().getElementByName(scenarioName);
						scenario.updatePolicyStats(sim.getSimulationParameters().getSchedulingPolicy().getPolicyName(), sim.getStatistics());

						if(isDebug)updateDebugResults(sim, scenario);

						for (String metric : metrcisNames) {
							Metric m = metricsModel.getElementByName(metric);
							m.updatePolicyStats(scenario, sim.getSimulationParameters().getSchedulingPolicy().getPolicyName(), sim.getStatistics());
						}
						progressModel.setValue(progressModel.getValue()+1);
					}
				});

				consumer.start();
				ISchedulingPolicy[] policies = null;	

				for (IEnergyProfile ep : energyProfiles) {
					List<String> policiesParams = new ArrayList<String>();

					policiesParams.add("EH_PFP_1 1");
					//policiesParams.add("EH_PFP_2 10");
					policiesParams.add("EH_PFP_4");
					//policiesParams.add("EH_PFP_5 10 90");
//					policiesParams.add("EH_PFP_6");
//					policiesParams.add("EH_PFP_7");

					policies = buildSchedulingPolicies(policiesParams);
					Scenario scenario = scenariosModel.getElementByName(ep.getMaxEnergyLevel()+ "_" + ep.getPowerRate());
					
//					systemsFile = "systems/old_"+ep.getMaxEnergyLevel()+"_"+ep.getPowerRate()+".txt";//BE CAREFULL
//					System.out.println("============================================================"+systemsFile);
					System.out.println("start ... producing");
					for (ISchedulingPolicy policy : policies) {
						TasksetReader reader = new TasksetReader(systemsFile);
						int nbEntries = 0;
						System.out.println("-----------" + policy.getPolicyName()+ " :  started");
						ITask[] tasks = null;
						while((tasks = reader.readTaskSet()) != null){
							nbEntries ++;
							final List<Integer> paramss = policy.getParameters()==null ? null : new LinkedList<Integer>(policy.getParameters());
							final ISchedulingPolicy policy2 = SchedulingPolicyFactory.getInstance(policy.getPolicyName());
							final ITask[] tasks2 = tasks;
							final int id = nbEntries;
							policy2.setParameters(paramss);
							final IEnergyProfile fep = EnergyProfileFactory.getInstance("defaultEnergyProfile", (int)ep.getPowerRate(), (int)ep.getMaxEnergyLevel(), (int)ep.getMinEnergyLevel(), (int)ep.getStartEnergyLevel());
							producer.produce(new Callable<Simulation>() {
								@Override
								public Simulation call() throws Exception {
									Simulation sim = new Simulation(buildSimulationParameters(fep, policy2, tasks2, id));
									sim.simulate();
									return sim;
								}
							});
						}
						reader.close();
						scenario.setSystemsCount(nbEntries) ;
						progressModel.setMaximum(nbEntries*policies.length*energyProfiles.length);
					}
				}
				System.out.println("finish producing");
				return null;
			}
		};
		task.execute();
	}


	private static ISchedulingPolicy[] buildSchedulingPolicies(	List<String> policiesParams) {
		ISchedulingPolicy[] policies = new ISchedulingPolicy[policiesParams.size()];
		int i = 0;
		for (String policy : policiesParams) {
			String[] params = policy.split(" ");
			String policyName = params[0];
			policies[i] = SchedulingPolicyFactory.getInstance(policyName);
			List<Integer> paramss = new ArrayList<Integer>();
			for (int j = 1; j < params.length; j++) {
				paramss.add(Integer.parseInt(params[j]));
			}
			policies[i].setParameters(paramss);
			i++;
		}
		return policies;
	}

	private static SimulationParameters buildSimulationParameters(
			IEnergyProfile energyProfile, ISchedulingPolicy policy, ITask[] tasks, int id) {
		ITaskSet taskset = policy.createTaskSet();
		taskset.setId(id);
		for (ITask task : tasks) {
			taskset.addTask(task.cloneTask());
		}
		return new SimulationParameters(policy, simulationDuration,	energyProfile, taskset,1, true);
	}

	public ScenariosChartModel getScenarioModel() {
		return scenariosModel;
	}

	public MetricsChartModel getMetricsChartModel(){
		return metricsModel;
	}


	public void setModel(ScenariosChartModel model) {
		this.scenariosModel = model;
	}


	public BoundedRangeModel getProgressModel() {
		return progressModel;
	}


	public static void main(String[] args) throws Exception {
		int nbSystems = 1000, pr=10;
		try{nbSystems = Integer.parseInt(args[0]);}
		catch (Exception e) {nbSystems = 1000;}


		String taskSetsSource = /*"results/"+*/nbSystems+"_systems_pr_"+pr;

		final Huge huge = new Huge(taskSetsSource);

		JFrame window = new JFrame("RTSS-");
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		Dimension d = new Dimension(screenSize);
		d.width  *= (5.0 / 6.0);
		d.height *= (5.0 / 6.0);
		window.setBounds((screenSize.width - d.width) / 2, (screenSize.height - d.height) / 2, d.width, d.height);
		window.setPreferredSize(d);
		window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		JTabbedPane tabbedPane = new JTabbedPane();

		JChartPane<Scenario> barchart = new JChartPane<Scenario>(huge.getScenarioModel());
		barchart.setChartRenderer(new BarChartRenderer());
		barchart.setDebug(false);
		JChartPane<Metric> lineChart = new JChartPane<Metric>(huge.getMetricsChartModel());
		lineChart.setChartRenderer(new LineChartRenderer());

		JProgressBar progressBar = new JProgressBar(huge.getProgressModel());
		JButton go = new JButton("GO");
		go.addActionListener(new ActionListener() {
			@Override	public void actionPerformed(ActionEvent e) {
				try {
					huge.run();
				} catch (Exception e1) {
					e1.printStackTrace();
				}
			}
		});
		JPanel root = new JPanel(new BorderLayout());
		tabbedPane.addTab("Scenarios", barchart);
		tabbedPane.addTab("Metrics", lineChart);
		root.add(tabbedPane,BorderLayout.CENTER);
		root.add(progressBar, BorderLayout.NORTH);
		root.add(go,BorderLayout.SOUTH);
		window.setContentPane(root);
		window.setVisible(true);
	}



}
